﻿using Hangfire.Client;
using Hangfire.Common;
using Hangfire.Server;
using Hangfire.States;
using Hangfire.Storage;
using System;
using System.Collections.Concurrent;
using System.Linq;
using System.Security.Cryptography;
using System.Text;

namespace Hangfire.HttpJob.CSRedis
{
    /// <summary>
    /// 同个任务取消并行执行，期间进来的任务不会等待，会被取消
    /// </summary>
    public class DisableMultipleQueueAttribute : JobFilterAttribute, IClientFilter, IServerFilter
    {
        private readonly TimeSpan _lockTime = TimeSpan.FromSeconds(10);
        public static readonly ConcurrentDictionary<string, IDisposable> LockDic = new ConcurrentDictionary<string, IDisposable>();

        public DisableMultipleQueueAttribute()
        {
        }

        public void OnCreated(CreatedContext filterContext)
        {
        }

        public void OnCreating(CreatingContext filterContext)
        {
            var key = GetJobKey(filterContext.Job);
            if (string.IsNullOrWhiteSpace(key))
                throw new ArgumentNullException("job为null");
            var connection = filterContext.Connection;
            var lockObj = connection.AcquireDistributedLock(GetLockKey(key), _lockTime);
            if (lockObj == null)
            {
                filterContext.Canceled = true;
                return;
            }

            LockDic.AddOrUpdate(key, lockObj, (key, value) => {
                LockDic[key].Dispose();
                return lockObj;
            });
        }

        public void OnPerformed(PerformedContext filterContext)
        {
            var key = GetJobKey(filterContext.BackgroundJob.Job);
            if (LockDic.TryRemove(key, out var lockObj))
                lockObj.Dispose();
            var args = filterContext.BackgroundJob.Job.Args;
            if (args.Count == 1 && args[0] is AgentJobContext)
                filterContext.Connection.SetJobParameter(filterContext.BackgroundJob.Id, "最终状态", "远程作业执行中");
        }

        public void OnPerforming(PerformingContext filterContext)
        {
            var args = filterContext.BackgroundJob.Job.Args;
            if (args.Count == 1 && args[0] is AgentJobContext agentContext)
                agentContext.TaskId = filterContext.BackgroundJob.Id;
        }
        #region private methods
        /// <summary>
        /// 获取KEY
        /// </summary>
        string GetJobKey(Job job)
        {
            if (job == null)
                return string.Empty;
            var key = job.ToString();
            if (job.Args != null && job.Args.Count > 0 && job.Args[0] is JobContext context)
                key = $"{context.JobName}|{job.Method.Name}|{string.Join('|', context.JobData.Select(m => $"{m.Key}_{m.Value}"))}";

            var hash = MD5.Create().ComputeHash(Encoding.UTF8.GetBytes(key));
            return Convert.ToBase64String(hash);
        }

        /// <summary>
        /// 分布式锁KEY
        /// </summary>>
        string GetLockKey(string key) => $"lock:{key}";

        /// <summary>
        /// 移除Hash
        /// </summary>
        void RemoveHash(IStorageConnection connection, string key)
        {
            using (var tran = connection.CreateWriteTransaction())
            {
                tran.RemoveHash(key);
                tran.Commit();
            }
        }
        #endregion
    }
}
